﻿extern alias alglibnet2;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace ClusterAggregation.Clusterers
{
    using Datum;
    using DataSets;
    /**
     * Partition Generator (helps API to make multiple partitions of 1 dataset)
     */
    public class CPartitionGenerator
    {
        /** basic distance function */
        private ISimilarity       m_distFunc;
        /** number of partitions to generate */
        private int               m_numberOfPartitions;
        /** locking object to make code re-entrable */
        private object            m_lock;
        /** smoothing parameters for Spectral clusterings */
        private double[]          m_smoothingParameters  = new double[] { 0, 0.01, 0.06,0.1, 0.3, 0.7, 1, 0.4 };
        /** clusterers to cluster with (generated via smoothing parameters) */
        private IClusterer[]      m_clusterers;
        /** k-means instance */
        private CKMeansImpl       m_kmeans;
        /** PAM instance */
        private CPamImpl          m_pam;

        /**
         * simple constructor 
         * @param distfunc (ISimilarity) [IN] is the default distance function we use. in case of null we'll use euclidean distance.
         */
        public CPartitionGenerator(ISimilarity distFunc = null)
        {
            m_distFunc = distFunc;
            m_kmeans = new CKMeansImpl();
            m_pam = new CPamImpl();

            if (distFunc == null)
            {
                m_distFunc = new CEuclideanDistanceSimilarityFunction();
            }

            m_numberOfPartitions = 5;
            m_lock = new object();

            m_clusterers = new IClusterer[m_smoothingParameters.Length];

            for (int i = 0; i < m_smoothingParameters.Length ; i++)
            {
                m_clusterers[i] = new CSpectralClusterer(m_distFunc, m_smoothingParameters[i]);
            }
        }

        /**
         * API to the generator. this function will generate partitions.
         * @param distfunc (ISimilarity) [IN] is the default distance function we use. in case of null we'll use the distance function in defined in our constructor.
         * @param arg (AData[]) [IN] the given data to cluster.
         * @param k (int) [IN] states how many clusters per partition.
         * @param useKmeans (bool) [IN] states if we should use K-Means to generate an extra partition.
         * @param usePam (bool) [IN] states if we should use PAM to generate an extra partition.
         * @param smoothers (double[]) [IN] states the smoothing parameters to use for Spectral Clustering.
         */
        public CPartition[] generatePartitions(AData[] arg, ISimilarity distFunc, int k, bool useKmeans, bool usePam, double[] smoothers)
        {
            LinkedList<CPartition> prts = new LinkedList<CPartition>();

            if (distFunc == null)
                distFunc = m_distFunc;

            if (useKmeans)
                prts.AddFirst(m_kmeans.cluster(arg, distFunc, k));

            if (useKmeans)
                prts.AddFirst(m_pam.cluster(arg, distFunc, k));

            for (int i = 0; i < smoothers.Length; i++)
            {
                IClusterer tmp = new CSpectralClusterer(distFunc, smoothers[i], true);
                prts.AddFirst(tmp.cluster(arg, distFunc, k));
            }

            return prts.ToArray();
        }


        /**
         * API to the generator. this function will generate partitions.
         * @param distfunc (ISimilarity) [IN] is the default distance function we use. in case of null we'll use the distance function in defined in our constructor.
         * @param arg (AData[]) [IN] the given data to cluster.
         * @param k (int) [IN] states how many clusters per partition.
         * @param useKmeans (bool) [IN] states if we should use K-Means to generate an extra partition.
         * @param usePam (bool) [IN] states if we should use PAM to generate an extra partition.
         * @param num_of_smoothers (int) [IN] states the how many smoothing parameters to use for Spectral Clustering (we use the default params, this param answers the question of how many of them)
         */
        public CPartition[] generatePartitions(AData[] arg, ISimilarity distFunc, int k, bool useKmeans, bool usePam, int num_of_smoothers)
        {
            LinkedList<CPartition> prts = new LinkedList<CPartition>();

            if (distFunc == null)
                distFunc = m_distFunc;

            if (useKmeans)
                prts.AddFirst(m_kmeans.cluster(arg, distFunc, k));
            if (useKmeans)
                prts.AddFirst(m_pam.cluster(arg, distFunc, k));

            for (int i = 0; i < num_of_smoothers; i++)
            {
                prts.AddFirst(m_clusterers[i % m_clusterers.Length].cluster(arg, distFunc, k));
            }

            return prts.ToArray();
        }


        /**
         * API to the generator. this function will generate partitions.
         * @param distfunc (ISimilarity) [IN] is the default distance function we use. in case of null we'll use the distance function in defined in our constructor.
         * @param arg (AData[]) [IN] the given data to cluster.
         * @param k (int) [IN] states how many clusters per partition.
         * @param num_of_smoothers (int) [IN] states the how many smoothing parameters to use for Spectral Clustering (we use the default params, this param answers the question of how many of them)
         */
        public CPartition[] generatePartitions(AData[] arg, ISimilarity distFunc, int k, int p)
        {
            if (distFunc == null)
                distFunc = m_distFunc;
            if (p == 0)
                p =  m_numberOfPartitions;

            CPartition[] partitions = new CPartition[p];

            lock(m_lock)
            {
                for (int i = 0; i < p; i++)
                {
                    partitions[i] = m_clusterers[i % m_clusterers.Length].cluster(arg, distFunc, k);
                }

                return partitions;
            } // end of lock
        }//end of func
    }
}
